package multithread.framework.tool;

import java.lang.annotation.Annotation;
import java.util.List;

import multithread.framework.annotation.NoneThreadGroup;
import multithread.framework.constant.XMLConfigConstant;
import multithread.framework.threadmethod.IContainer;
import multithread.framework.threadmethod.IParser;
import multithread.framework.threadmethod.IThreadMethod;
import multithread.framework.threadmethod.ParserFactory;
import multithread.framework.threadmethod.ThreadMethod;
import multithread.framework.threadmethod.ThreadMethodContainer;
import multithread.framework.threadmethod.ThreadMethodGroup;
import multithread.framework.utility.Utilities;
import multithread.framework.xml.CaseElement;
import multithread.framework.xml.CommonElement;
import multithread.framework.xml.GroupElement;
import multithread.framework.xml.MethodElement;

import org.jdom.Comment;
import org.jdom.Document;

/**
 * Generate XML configuration for test case
 * @author dailey_dai
 *
 */
public class TestCaseGenerator implements IGenerator<Document, Object> {

	private IParser<? extends IContainer> parser;
	private XMLGenerator generator = null;
	private Object testcase = null;

	public TestCaseGenerator(XMLGenerator generator) {
		parser = ParserFactory.createThreadMethodParser();
		this.generator = generator;
	}

	@Override
	public void write(Object testcase) {
		if (testcase != null) {
			this.testcase = testcase;
		}
	}

	@Override
	public Document generate() {
		ThreadMethodContainer container = (ThreadMethodContainer) parser
				.parse(testcase);
		Document casedoc = buildTestCaseDocumet(container);
		generator.write(casedoc);
		generator.generate();
		return casedoc;
	}

	/**
	 * create document for test case from {@link ThreadMethodContainer}
	 * 
	 * @param container
	 *            ThreadMethodContainer
	 * @return Document
	 */
	private Document buildTestCaseDocumet(ThreadMethodContainer container) {
		// root element
		CaseElement caseElement = new CaseElement(container);
		/**
		 * add common element
		 */
		caseElement.addCommonElement(createCommonElement(container));
		caseElement.addGroupElement(createVirtualGroupElement(container));
		/**
		 * add group type is {@link XMLConfigConstant.THREAD_GROUP_FOR_TYPE}
		 */
		for (ThreadMethodGroup methodGroup : container.getThreadGroups()) {
			caseElement.addGroupElement(new GroupElement(methodGroup));
		}
		return caseElement.validate() ? addComment(new Document(caseElement))
				: null;
	}

	/**
	 * add some comment for document
	 * 
	 * @param document
	 *            Document
	 * @return Document
	 */
	private Document addComment(Document document) {
		return document.addContent(
				0,
				new Comment(
						"This XML is generated by test case generator tool."))
				.addContent(1, new Comment(Utilities.now()));
	}

	/**
	 * create common element such as {@link XMLConfigConstant.ALLBEFORE_ELEMENT}
	 * 
	 * @param container
	 *            ThreadMethodContainer
	 * @return CommonElement
	 */
	private CommonElement createCommonElement(ThreadMethodContainer container) {
		CommonElement common = new CommonElement();
		for (Class<? extends Annotation> annotationTypeClz : Utilities.COMMONANNOTATIONS) {
			List<IThreadMethod> threadMethodList = container
					.getThreadMethods(annotationTypeClz);
			if (threadMethodList.size() > 0) {
				for (IThreadMethod threadMethod : threadMethodList) {
					common.addMethodElement(new MethodElement(
							(ThreadMethod) threadMethod, true),
							annotationTypeClz);
				}
			}
		}
		return common;

	}

	/**
	 * group type is {@link XMLConfigConstant.NONE_THREAD_GROUP_TYPE}
	 * 
	 * @param container
	 *            ThreadMethodContainer
	 * @return GroupElement
	 */
	private GroupElement createVirtualGroupElement(
			ThreadMethodContainer container) {
		GroupElement group = new GroupElement(null,
				XMLConfigConstant.NONE_THREAD_GROUP_TYPE);
		for (IThreadMethod threadMethod : container
				.getThreadMethods(NoneThreadGroup.class)) {
			group.addMethodElement(new MethodElement(
					(ThreadMethod) threadMethod));
		}
		return group;
	}

}
